home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / win / pascal / ezdpmi.exe / EZDPMI.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1993-03-21  |  21.5 KB  |  636 lines

  1. {==EZDPMI=============================================================
  2.  
  3. A protected mode DPMI access unit. Provides an easy-to-use interface
  4. to the common application-oriented requirements for DPMI: the ability
  5. to interface real-mode drivers and TSRs from protected mode using DOS
  6. memory.
  7.  
  8. EZDPMI is Copyright (c) 1993 by  Julian M. Bucknall
  9.  
  10. VERSION HISTORY
  11. 21Mar93 JMB 1.00 initial release
  12. ======================================================================}
  13.  
  14. {=====================================================================
  15.  
  16. From numerous threads in the BPASCAL forum of CompuServe, I felt that
  17. there was a real need for a simple unit to access the main features of
  18. DPMI that would be required for a middling to difficult application
  19. program. Having played around with DPMI in both Windows and now in the
  20. new protected mode Borland Pascal, I had a bunch of routines under my
  21. belt that I'd used in real programs, and even written about in the
  22. British programming magazine .EXE in November 1992.
  23.  
  24. My chief consideration in writing these routines was to make things
  25. simple for myself: for example I didn't want to use the DPMI register
  26. structure, I wanted to use the more familiar Registers type (or
  27. TRegisters if you use the WinDOS unit); I wanted to mimic the calling
  28. conventions of the units supplied with Borland Pascal; I wanted an
  29. easy life!
  30.  
  31. The world of protected mode programming brings many benefits (mainly
  32. the very large heap!), but makes other activities much harder (making
  33. sure you have valid pointers is a good one - the number of times
  34. Run-Time error 216 occurs these days!). One of the main problems with
  35. writing programs in protected mode (Windows or no) is interfacing with
  36. real mode DOS, real mode BIOS and real mode DOS drivers/TSRs. The DPMI
  37. manager translates various calls to these interrupts, and others it
  38. leaves well alone (otherwise it would be at least as large as all of
  39. them put together).
  40.  
  41. As an aside, when you call some real-mode interrupt via the Intr
  42. procedure various things happen. Firstly you perform a protected mode
  43. interrupt, not a real mode interrupt (the DPMI manager keeps two
  44. tables of interrupt tables, one for real mode and one for protected
  45. mode). If the interrupt gets handled by a protected mode process that
  46. is not the DPMI manager itself then the latter does nothing - the
  47. protected mode process does it all. If the interrupt points into the
  48. DPMI manager itself then it must make a decision: can it handle the
  49. interrupt, or must it pass the interrupt one into real mode? For the
  50. majority of DOS functions for example, the DPMI manager will handle
  51. the interrupt itself (obviously as a program will make many of them
  52. and it would be more efficient that way). If the DPMI manager doesn't
  53. know about the interrupt, it switches the machine into real mode,
  54. calls the real mode interrupt handler, and on return, switches back
  55. into protected mode and returns to your program. There is one other
  56. action that occurs, and that is the DPMI manager ensures that on
  57. return from real mode the segment registers contain valid selector
  58. values.
  59.  
  60. OK. Now imagine you have a Whizzo-matic Digital Scanner and Coffee
  61. Maker attached to your PC, and that the API for the driver for this
  62. box of tricks states that interrupt $68 subfunction $01 (in AX) will
  63. fill a buffer you supply (address in ES:DI) with its current state.
  64. In a real mode program you'd write something along the lines of
  65.  
  66.   procedure GetWhizzoState(var State : TWhizzoState);
  67.     var
  68.       R : Registers;
  69.     begin
  70.       R.ax := $01;
  71.       R.es := Seg(State);
  72.       R.di := Ofs(State);
  73.       Intr($68, R);
  74.     end;
  75.  
  76. No sweat. In protected mode, you have a problem in that the buffer
  77. StateBuffer is going to be in a part of memory unreachable from real
  78. mode, and indeed the value of ES is going to be a selector value not
  79. a segment value. What you'd ideally need is a chunk of memory that
  80. could be addressed from both real mode and protected mode _at the
  81. same time_.
  82.  
  83. Enter the two complementary functions GetDOSMem and FreeDOSMem. Like
  84. GetMem and FreeMem they allocate and deallocate memory via pointers,
  85. but unlike these two, they make sure that it is in DOS real mode
  86. memory. GetDOSMem allocates Size bytes of DOS memory and returns both
  87. a real mode pointer and a protected mode pointer to that same block
  88. of memory. FreeDOSMem takes the protected mode pointer to some
  89. previously allocated DOS memory, and frees it and the selector to it.
  90. Be aware that in Windows at least the amount of DOS memory is small
  91. and so you shouldn't hang on to it for longer than you need (other
  92. processes are also desirous of some of it).
  93.  
  94. Our code snippet in protected mode would become:
  95.  
  96.   procedure GetWhizzoState(var State : TWhizzoState);
  97.     type
  98.       PWhizzoState : ^TWhizzoState;
  99.       PtrRec = record Ofs, Seg : word; end;
  100.     var
  101.       R : TRegisters;
  102.       RealState,                (* Realmode pointer to buffer *)
  103.       ProtState : PWhizzoState; (* ...and Protected mode pointer *)
  104.     begin
  105.       if GetDOSMem(RealState, ProtState, sizeof(TWhizzoState)) then
  106.         begin
  107.           FillChar(R, sizeof(R), 0);
  108.           R.ax := $01;
  109.           R.es := PtrRec(RealState).Seg;
  110.           R.di := PtrRec(RealState).Ofs;
  111.           Intr($68, R);
  112.           Move(ProtState^, State, sizeof(TWhizzoState));
  113.           if not FreeDOSMem(ProtState) then (* nothing *) ;
  114.         end;
  115.     end;
  116.  
  117. Note that we have to get the Whizzo state into our own buffer first
  118. and then copy that into the program's own buffer. Note also that we
  119. free up the DOS memory immediately. Remember that as the interrupt
  120. expects a real mode buffer we pass the real mode address of our buffer
  121. to the interrupt, but we in our program use the protected mode pointer
  122. to access the memory.
  123.  
  124. However there is still a bug in this routine. It will cause a GPF
  125. within the Intr procedure because we are passing a real mode segment
  126. value in a protected mode segment (ie selector) register. We need to
  127. _directly_ call the real mode interrupt ourselves, which Intr doesn't
  128. do for us.
  129.  
  130. Enter the RealIntr procedure. It takes the same parameters as Intr but
  131. calls the real mode interrupt directly. Our routine now becomes:
  132.  
  133.   procedure GetWhizzoState(var State : TWhizzoState);
  134.     type
  135.       PWhizzoState : ^TWhizzoState;
  136.       PtrRec = record Ofs, Seg : word; end;
  137.     var
  138.       R : TRegisters;
  139.       RealState,                (* Realmode pointer to buffer *)
  140.       ProtState : PWhizzoState; (* ...and Protected mode pointer *)
  141.     begin
  142.       if GetDOSMem(RealState, ProtState, sizeof(TWhizzoState)) then
  143.         begin
  144.           FillChar(R, sizeof(R), 0);
  145.           R.ax := $01;
  146.           R.es := PtrRec(RealState).Seg;
  147.           R.di := PtrRec(RealState).Ofs;
  148.           RealIntr($68, R);
  149.           Move(ProtState^, State, sizeof(TWhizzoState));
  150.           if not FreeDOSMem(ProtState) then (* nothing *) ;
  151.         end;
  152.     end;
  153.  
  154. Not too bad, huh.
  155.  
  156. Now suppose instead that the Whizzo-matic driver returned the address
  157. of its state buffer directly in ES:DI (we don't have to provide a
  158. buffer for it at all). Our real mode routine might look like
  159.  
  160.   procedure GetWhizzoState(var State : TWhizzoState);
  161.     type
  162.       PWhizzoState : ^TWhizzoState;
  163.     var
  164.       R : TRegisters;
  165.       RealState : PWhizzoState;
  166.     begin
  167.       R.ax := $01;
  168.       Intr($68, R);
  169.       RealState := Ptr(R.es, R.di);
  170.       Move(RealState^, State, sizeof(TWhizzoState));
  171.     end;
  172.  
  173. Our first stab in protected mode would be to replace the Intr call
  174. with a RealIntr call, but the routine would fail later on the Move,
  175. because the interrupt would have returned a real mode address, _not_
  176. a protected mode one. We need a way of mapping a protected mode
  177. pointer onto a given real mode pointer. Recall that the Seg0040
  178. selector defines a protected mode selector to the $0040 BIOS data
  179. segment (and similarly SegB000, SegB800, etc) - we need something
  180. similar.
  181.  
  182. Enter the GetMappedDPMIPtr function. Given a real mode pointer to a
  183. memory block of Size bytes, it will create a (read/write) protected
  184. mode pointer to that same memory block. The FreeMappedDPMIPtr will
  185. free up the protected mode pointer so created (recall that in
  186. protected mode there are a limited number of selectors - we need to
  187. conserve this resource in our programs). So our better stab in
  188. protected mode would be
  189.  
  190.   procedure GetWhizzoState(var State : TWhizzoState);
  191.     type
  192.       PWhizzoState : ^TWhizzoState;
  193.     var
  194.       R : TRegisters;
  195.       RealState,                (* Realmode pointer to buffer *)
  196.       ProtState : PWhizzoState; (* ...and Protected mode pointer *)
  197.     begin
  198.       FillChar(R, sizeof(R), 0);
  199.       R.ax := $01;
  200.       RealIntr($68, R);
  201.       RealState := Ptr(R.es, R.di);
  202.       if GetMappedDPMIPtr(ProtState, RealState, sizeof(TWhizzoState)) then
  203.         begin
  204.           Move(ProtState^, State, sizeof(TWhizzoState));
  205.           if not FreeMappedDPMIPtr(ProtState) then (* nothing *) ;
  206.         end;
  207.     end;
  208.  
  209. The other routines in the EZDPMI unit came out of other
  210. considerations. The RealCall function calls a real mode routine
  211. directly given its address. I needed it for the DOS uppercase function
  212. - recall that interrupt $21 subfunction $38 returns a bunch of
  213. country-specific data (eg how dates are displayed, what the currency
  214. character is and where it appears when used with an amount, and so
  215. on). One of the items returned is the address of a routine that will
  216. uppercase a character in AL (we're talking characters with accents,
  217. umlauts, cedillas here - characters above #127). Well it so happens
  218. that this is a real mode routine, and has to be called in real mode.
  219. RealCall takes the roughly the same parameters as RealIntr (and Intr)
  220. - the first parameter is however the real mode address of a routine -
  221. and calls the routine in real mode.
  222.  
  223. The example above gets coded as follows:
  224.  
  225.   var
  226.     DOSUpCaseRoutine : RealProc;
  227.  
  228.   procedure SetDOSUpCaseRoutine;
  229.     var
  230.       R : TRegisters;
  231.       Buf : array [0..63] of word;
  232.     begin
  233.       FillChar(R, sizeof(R), 0);
  234.       R.ax := $3800;
  235.       R.ds := Seg(Buf);
  236.       R.dx := Ofs(Buf);
  237.       Intr($21, R);
  238.       DOSUpCaseRoutine := Ptr(Buf[10], Buf[9]);
  239.     end;
  240.  
  241.   function DOSUpperCase(Ch : char) : char;
  242.     var
  243.       R : TRegisters;
  244.     begin
  245.       FillChar(R, sizeof(R), 0);
  246.       R.al := ord(Ch);
  247.       RealCall(DOSUpCaseRoutine, R);
  248.       DOSUpperCase := char(R.al);
  249.     end;
  250.  
  251. And the Seg0040 value? Well, BPW doesn't define it, and I needed it...
  252.  
  253. The legal bit now. I am releasing this unit as freeware. In other
  254. words you don't have to pay me for using it in a compiled executable
  255. application program, but I retain all copyright in it and in the
  256. source code within. You cannot distribute the EZDPMI source with source
  257. of your own (as part of a programming library for example) without
  258. including my copyright notice and without paying money to the charity
  259. of my choice for the pleasure of doing so.
  260.  
  261. Enjoy. If you have any problems, you can get in touch with me via
  262. CompuServe on [100116,1572]. Similarly, if you'd like some extensions
  263. to it get in touch and I'll see what I can do.
  264.  
  265.                              Julian M. Bucknall, London UK, March 1993
  266.  
  267.  
  268. EZDPMI Copyright (c) 1993 Julian M. Bucknall
  269. ======================================================================}
  270.  
  271. unit EzDPMI;
  272.  
  273. {------Common compiler switches---------------------------------------}
  274. {$A+   Word align variables }
  275. {$B-   Short-circuit boolean expressions }
  276. {$F+   Force Far calls }
  277. {$I-   No I/O checking }
  278. {$N+   Allow coprocessor instructions }
  279. {$P+   Open parameters enabled }
  280. {$Q-   No integer overflow checking }
  281. {$R-   No range checking }
  282. {$S-   No stack checking }
  283. {$T-   @ operator is NOT typed }
  284. {$V-   Disable var string checking }
  285. {$X+   Enable extended syntax }
  286. {$IFDEF DEBUG}
  287. {$D+,L+,Y+  Enable debug information }
  288. {$ENDIF}
  289. {---------------------------------------------------------------------}
  290.  
  291. {------Real mode compiler switches------------------------------------}
  292. {$IFDEF MSDOS}
  293. {$E+   Enable coprocessor emulation }
  294. {$G-   8086 type instructions }
  295. {$O-   Do NOT allow overlays }
  296. {$DEFINE RealMode}
  297. {$UNDEF  ProtMode}
  298. {$ENDIF}
  299. {---------------------------------------------------------------------}
  300.  
  301. {------Protected mode compiler switches-------------------------------}
  302. {$IFDEF DPMI}
  303. {$E+   Enable coprocessor emulation }
  304. {$G+   80286+ type instructions }
  305. {$UNDEF  RealMode}
  306. {$DEFINE ProtMode}
  307. {$ENDIF}
  308. {---------------------------------------------------------------------}
  309.  
  310. {------Windows compiler switches--------------------------------------}
  311. {$IFDEF WINDOWS}
  312. {$G+   80286+ type instructions }
  313. {$K+   Use smart callbacks
  314. {$W-   No Windows realmode stack frame }
  315. {$UNDEF  RealMode}
  316. {$DEFINE ProtMode}
  317. {$ENDIF}
  318. {---------------------------------------------------------------------}
  319.  
  320. {$IFDEF MSDOS} Error - protected mode only {$ENDIF}
  321.  
  322. INTERFACE
  323.  
  324. uses WinDOS,
  325. {$IFDEF Windows}
  326.      WinProcs
  327. {$ELSE}
  328.      WinAPI
  329. {$ENDIF}
  330.      ;
  331.  
  332. {$IFDEF Windows}
  333. var
  334.   Seg0040 : word;     { To access the BIOS data area in Windows }
  335. {$ENDIF}
  336.  
  337. type
  338.   RealProc = procedure;
  339.  
  340. {=DOSGetMem===========================================================
  341. Allocates and returns the real and protected mode pointers to a DOS
  342. memory block of Size bytes in the first 1Mb. Returns true if
  343. successful, false otherwise.
  344. 21Mar93 JMB
  345. ======================================================================}
  346. function DOSGetMem(var RealPtr, ProtPtr; Size : word) : boolean;
  347.  
  348. {=DOSFreeMem==========================================================
  349. Deallocates a DOS memory block allocated with DOSGetMem. Returns true
  350. if successful, false otherwise.
  351. 21Mar93 JMB
  352. ======================================================================}
  353. function DOSFreeMem(ProtPtr : pointer) : boolean;
  354.  
  355. {=RealIntr============================================================
  356. Calls the real mode interrupt IntNo. Unlike Intr this guarantees a
  357. real mode interrupt. Intr performs a protected mode interrupt first,
  358. which the DPMI server may pass thru to the real mode interrupt.
  359. Returns true if successful, false otherwise.
  360. 21Mar93 JMB
  361. ======================================================================}
  362. function RealIntr(IntNo : byte; var Regs : TRegisters) : boolean;
  363.  
  364. {=RealCall============================================================
  365. Calls the real mode Routine procedure (must be a far procedure and
  366. return with RETF). No stack is transferred, the routine is assumed to
  367. accept its parameters from the registers.
  368. Returns true if successful, false otherwise.
  369. 21Mar93 JMB
  370. ======================================================================}
  371. function RealCall(Routine : RealProc; var Regs : TRegisters) : boolean;
  372.  
  373. {=GetMappedDPMIPtr====================================================
  374. Given a real mode pointer to a DOS memory block, returns a protected
  375. mode pointer mapped to the same block.
  376. Returns true if successful, false otherwise.
  377. 21Mar93 JMB
  378. ======================================================================}
  379. function GetMappedDPMIPtr(var ProtPtr; RealPtr : pointer; Size : word)
  380.            : boolean;
  381.  
  382. {=FreeMappedDPMIPtr===================================================
  383. Frees a protected mode pointer (ie selector) that was allocated by
  384. GetMappedDPMIPtr.
  385. Returns true if successful, false otherwise.
  386. 21Mar93 JMB
  387. ======================================================================}
  388. function FreeMappedDPMIPtr(ProtPtr : pointer) : boolean;
  389.  
  390. IMPLEMENTATION
  391.  
  392. var
  393.   ExitSave : pointer;
  394.  
  395. function DOSGetMem(var RealPtr, ProtPtr; Size : word) : boolean;
  396.   type
  397.     LI = record LoWord, HiWord : word; end;
  398.   var
  399.     RealMode : pointer absolute RealPtr;
  400.     ProtMode : pointer absolute ProtPtr;
  401.     Result : longint;
  402.   begin
  403.     Result := GlobalDOSAlloc(Size);
  404.     if (Result <> 0) then
  405.       begin
  406.         RealMode := Ptr(LI(Result).HiWord, 0);
  407.         ProtMode := Ptr(LI(Result).LoWord, 0);
  408.         DOSGetMem := true;
  409.       end
  410.     else DOSGetMem := false;
  411.   end;
  412.  
  413. function DOSFreeMem(ProtPtr : pointer) : boolean;
  414.   type
  415.     SO = record O, S : word; end;
  416.   begin
  417.     DOSFreeMem := GlobalDOSFree(SO(ProtPtr).S) = 0;
  418.   end;
  419.  
  420. function RealIntr(IntNo : byte; var Regs : TRegisters) : boolean;
  421. assembler;
  422.   type
  423.     TDPMIRegisters = record
  424.       EDI, ESI, EBP, Res, EBX, EDX, ECX, EAX  : longint;
  425.       Flags, ES, DS, FS, GS, IP, CS, SP, SS   : word;
  426.     end;
  427.   var
  428.     DPMIregs : TDPMIRegisters;
  429.   asm
  430.     push ds
  431.     lds si, Regs
  432.     mov ax, ss;  mov es, ax;  lea di, DPMIregs
  433.     cld
  434.     xor ax, ax
  435.     add si, 12         { EDI }
  436.     movsw;  stosw
  437.     sub si, 4          { ESI }
  438.     movsw;  stosw
  439.     sub si, 4          { EBP }
  440.     movsw;  stosw
  441.     stosw;  stosw      { Res }
  442.     sub si, 8          { EBX }
  443.     movsw;  stosw
  444.     add si, 2          { EDX }
  445.     movsw;  stosw
  446.     sub si, 4          { ECX }
  447.     movsw;  stosw
  448.     sub si, 6          { EAX }
  449.     movsw;  stosw
  450.     add si, 16         { Flags }
  451.     movsw;
  452.     sub si, 4          { ES }
  453.     movsw;
  454.     sub si, 4          { DS }
  455.     movsw;
  456.     mov cx, 6          { FS, GS, IP, CS, SP, SS }
  457.     rep stosw
  458.     lea di, DPMIregs
  459.     mov ax, 0300h      { DPMI code to simulate intr }
  460.     xor bx, bx         { Set BH to zero (and BL) }
  461.     mov bl, IntNo      { Save interrupt number }
  462.     xor cx, cx         { No stack words to copy }
  463.     int 31h            { DPMI Services }
  464.     mov ax, 0
  465.     jc @@ExitPoint     { Error? - yes }
  466.     les di, Regs
  467.     mov ax, ss;  mov ds, ax;  lea si, DPMIregs
  468.     cld
  469.     add si, 28; movsw  { AX }
  470.     sub si, 14; movsw  { BX }
  471.     add si, 6;  movsw  { CX }
  472.     sub si, 6;  movsw  { DX }
  473.     sub si, 14; movsw  { BP }
  474.     sub si, 6;  movsw  { SI }
  475.     sub si, 6;  movsw  { DI }
  476.     add si, 34; movsw  { DS }
  477.     sub si, 4;  movsw  { ES }
  478.     sub si, 4;  movsw  { Flags }
  479.     mov ax, 1
  480.   @@ExitPoint:
  481.     pop ds
  482.   end;
  483.  
  484. function RealCall(Routine : RealProc; var Regs : TRegisters) : boolean;
  485. assembler;
  486.   type
  487.     TDPMIRegisters = record
  488.       EDI, ESI, EBP, Res, EBX, EDX, ECX, EAX  : longint;
  489.       Flags, ES, DS, FS, GS, IP, CS, SP, SS   : word;
  490.     end;
  491.   var
  492.     DPMIregs : TDPMIRegisters;
  493.   asm
  494.     push ds
  495.     lds si, Regs
  496.     mov ax, ss;  mov es, ax;  lea di, DPMIregs
  497.     cld
  498.     xor ax, ax
  499.     add si, 12         { EDI }
  500.     movsw;  stosw
  501.     sub si, 4          { ESI }
  502.     movsw;  stosw
  503.     sub si, 4          { EBP }
  504.     movsw;  stosw
  505.     stosw;  stosw      { Res }
  506.     sub si, 8          { EBX }
  507.     movsw;  stosw
  508.     add si, 2          { EDX }
  509.     movsw;  stosw
  510.     sub si, 4          { ECX }
  511.     movsw;  stosw
  512.     sub si, 6          { EAX }
  513.     movsw;  stosw
  514.     add si, 16         { Flags }
  515.     movsw;
  516.     sub si, 4          { ES }
  517.     movsw;
  518.     sub si, 4          { DS }
  519.     movsw;
  520.     mov cx, 6          { FS, GS, IP, CS, SP, SS }
  521.     rep stosw
  522.     sub di, 8
  523.     mov ax, Routine.Word[0]  { Routine's real IP }
  524.     stosw
  525.     mov ax, Routine.Word[2]  { Routine's real CS }
  526.     stosw
  527.     lea di, DPMIregs
  528.     mov ax, 0301h      { DPMI code to simulate call }
  529.     xor bx, bx         { Set BH to zero (and BL) }
  530.     xor cx, cx         { No stack words to copy }
  531.     int 31h            { DPMI Services }
  532.     mov ax, 0
  533.     jc @@ExitPoint     { Error? - yes }
  534.     les di, Regs
  535.     mov ax, ss;  mov ds, ax;  lea si, DPMIregs
  536.     cld
  537.     add si, 28; movsw  { AX }
  538.     sub si, 14; movsw  { BX }
  539.     add si, 6;  movsw  { CX }
  540.     sub si, 6;  movsw  { DX }
  541.     sub si, 14; movsw  { BP }
  542.     sub si, 6;  movsw  { SI }
  543.     sub si, 6;  movsw  { DI }
  544.     add si, 34; movsw  { DS }
  545.     sub si, 4;  movsw  { ES }
  546.     sub si, 4;  movsw  { Flags }
  547.     mov ax, 1
  548.   @@ExitPoint:
  549.     pop ds
  550.   end;
  551.  
  552. function GetMappedDPMIPtr(var ProtPtr; RealPtr : pointer; Size : word)
  553.            : boolean;
  554. assembler;
  555.   asm
  556.     xor ax, ax               { Get an LDT descriptor & selector for it }
  557.     mov cx, 1
  558.     int 31h
  559.     jc @@Error
  560.     xchg ax, bx
  561.     xor ax, ax               { Set descriptor to real address }
  562.     mov dx, RealPtr.Word[2]
  563.     mov al, dh
  564.     mov cl, 4
  565.     shr ax, cl
  566.     shl dx, cl
  567.     xchg ax, cx
  568.     mov ax, 7
  569.     int 31h
  570.     jc @@Error
  571.     mov ax, 8                { Set descriptor to limit Size bytes }
  572.     xor cx, cx
  573.     mov cx, Size
  574.     add cx, RealPtr.Word[0]
  575.     jnc @@1
  576.     xor cx, cx
  577.     dec cx
  578.   @@1:
  579.     int 31h
  580.     jc @@Error
  581.     cld                      { Save selector:offset in ProtPtr }
  582.     les di, ProtPtr
  583.     mov ax, RealPtr.Word[0]
  584.     stosw
  585.     xchg ax, bx
  586.     stosw
  587.     mov ax, 1
  588.     jmp @@Exit
  589.   @@Error:
  590.     xor ax, ax
  591.   @@Exit:
  592.   end;
  593.  
  594.  
  595. function FreeMappedDPMIPtr(ProtPtr : pointer) : boolean;
  596. assembler;
  597.   asm
  598.     mov ax, 1
  599.     mov bx, ProtPtr.Word[2]
  600.     int 31h
  601.     mov ax, 0
  602.     jc @@Error
  603.     inc ax
  604.   @@Error:
  605.   end;
  606.  
  607. {$IFDEF Windows}
  608. {=CleanupDPMI=========================================================
  609. Removes the Seg0040 selector for Windows.
  610. 21Mar93 JMB
  611. ======================================================================}
  612. procedure CleanupDPMI; far;
  613.   var
  614.     PP : pointer;
  615.   begin
  616.     ExitProc := ExitSave;
  617.     PP := Ptr(Seg0040, 0);
  618.     FreeMappedDPMIPtr(PP);
  619.   end;
  620.  
  621. {=Initialisation======================================================
  622. Sets up the Seg0040 selector for Windows.
  623. 21Mar93 JMB
  624. ======================================================================}
  625. type
  626.   SO = record O, S : word; end;
  627. var
  628.   PP : pointer;
  629. begin
  630.   GetMappedDPMIPtr(PP, Ptr($40, 0), $400); {1024 byte limit}
  631.   Seg0040 := SO(PP).S;
  632.   ExitSave := ExitProc;
  633.   ExitProc := @CleanupDPMI;
  634. {$ENDIF}
  635. end.
  636.